// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.commons.ui.swt.table;

import java.util.Arrays;
import java.util.List;

import org.dengues.commons.IDenguesSharedImage;
import org.dengues.commons.i18n.Messages;
import org.dengues.commons.sql.data.DataSetRow;
import org.dengues.commons.utils.ImageUtil;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 */
public class TableViewerWithMultipage extends Composite {

    /**
     * Qiang.Zhang.Adolf@gmail.com DataEditorComposite class global comment. Detailled comment <br/>
     * 
     */
    class ColumnSelectionListener extends SelectionAdapter {

        private int colIndex = 0;

        private int updown = 1;

        private ViewerComparator getCurrentComparator() {
            return new ViewerComparator() {

                /*
                 * (non-Javadoc)
                 * 
                 * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
                 * java.lang.Object, java.lang.Object)
                 */
                @Override
                public int compare(Viewer viewer, Object e1, Object e2) {

                    ComparatorFactory.UP_DOWN = updown;
                    DataSetRow row1 = (DataSetRow) e1;
                    DataSetRow row2 = (DataSetRow) e2;
                    if (colIndex == 0) {
                        int rowIndex = row1.getRowIndex();
                        int rowIndex2 = row2.getRowIndex();
                        return ComparatorFactory.getComparatorFrom(int.class).compare(rowIndex % pageItems + 1,
                                rowIndex2 % pageItems + 1);
                    } else {
                        Object objectValue = row2.getObjectValue(colIndex - 1);
                        Object objectValue2 = row1.getObjectValue(colIndex - 1);
                        int compare = ComparatorFactory.getComparatorFrom(objectValue.getClass()).compare(objectValue2,
                                objectValue);
                        return compare;
                    }
                }
            };
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
         */
        @Override
        public void widgetSelected(SelectionEvent e) {
            updown = -1 * updown;
            TreeColumn fColumn1 = (TreeColumn) e.widget;
            Tree parent2 = fColumn1.getParent();
            for (int i = 0; i < parent2.getColumnCount(); i++) {
                TreeColumn col = parent2.getColumn(i);
                if (col == fColumn1) {
                    colIndex = i;
                    break;
                }
            }
            treeViewer.setComparator(getCurrentComparator());
            setColumnSorting(fColumn1, updown);
        }

    }

    /**
     * Qiang.Zhang.Adolf@gmail.com TableViewerWithMultipage class global comment. Detailled comment <br/>
     * 
     * @since 2007-10-24 下午10:36:13
     */
    class CellModifier implements ICellModifier {

        public boolean canModify(Object element, String property) {
            if ("i_d_".equals(property)) { //$NON-NLS-1$
                return false;
            }
            return true;
        }

        public Object getValue(Object element, String property) {
            if (element instanceof DataSetRow) {
                DataSetRow row = (DataSetRow) element;
                String[] props = (String[]) treeViewer.getColumnProperties();
                int indexOf = Arrays.asList(props).indexOf(property);
                return row.getObjectValue(indexOf - 1);
            }
            return ""; //$NON-NLS-1$
        }

        public void modify(Object element, String property, Object value) {
            if (element instanceof TreeItem) {
                TreeItem item = (TreeItem) element;
                if (item.getData() instanceof DataSetRow) {
                    DataSetRow row = (DataSetRow) item.getData();
                    String[] props = (String[]) treeViewer.getColumnProperties();
                    int indexOf = Arrays.asList(props).indexOf(property);
                    row.setValue(indexOf - 1, value);
                }
            }
            treeViewer.refresh();
        }

    }

    /**
     * Qiang.Zhang.Adolf@gmail.com TableViewerWithMultipage class global comment. Detailled comment <br/>
     * 
     * @since 2007-10-24 下午10:36:17
     */
    class TableLabelProvider extends LabelProvider implements ITableLabelProvider {

        public String getColumnText(Object element, int columnIndex) {
            if (element instanceof DataSetRow) {
                DataSetRow row = (DataSetRow) element;
                if (columnIndex == 0) {
                    return String.valueOf(row.getRowIndex() + 1);
                }
                Object[] pvalues = row.getPvalues();
                Object object = pvalues[columnIndex - 1];
                return object == null ? "" : object.toString(); //$NON-NLS-1$
            }
            return ""; //$NON-NLS-1$
        }

        public Image getColumnImage(Object element, int columnIndex) {
            return null;
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com TableViewerWithMultipage class global comment. Detailled comment <br/>
     * 
     * @since 2007-10-24 下午10:29:08
     */
    class ContentProvider implements ITreeContentProvider {

        public Object[] getElements(Object inputElement) {
            if (inputElement instanceof List) {
                return ((List) inputElement).toArray();
            }
            return new Object[0];
        }

        public void dispose() {
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
         */
        public Object[] getChildren(Object parentElement) {
            return getElements(parentElement);
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
         */
        public Object getParent(Object element) {
            return null;
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
         */
        public boolean hasChildren(Object element) {
            return false;
        }
    }

    public static int firstColumnWidth = 20;

    public static int otherColumnWidth = 150;

    private final Tree tree;

    private List<DataSetRow> items;

    private int totalItems;

    private int pageItems;

    private int pages;

    private int nowPage;

    private Button btnLast;

    private Button btnFirst;

    private Button btnNext;

    private Button btnPre;

    private final Label lblPages;

    private final Label lblItems;

    private final TreeViewer treeViewer;

    private final Composite group;

    private boolean canModify;

    /**
     * @return
     */
    private boolean init() {
        pageItems = 10;
        return true;
    }

    /**
     * Create the composite
     * 
     * @param parent
     * @param canModify
     */
    public TableViewerWithMultipage(Composite parent, boolean canModify) {
        super(parent, SWT.NONE);
        this.canModify = canModify;
        GridLayout layout = new GridLayout();
        layout.marginLeft = 0;
        layout.marginTop = 0;
        layout.marginBottom = 0;
        layout.horizontalSpacing = 0;
        layout.verticalSpacing = 0;
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        layout.marginRight = 0;

        setLayout(layout);
        GridData gridData = new GridData(GridData.FILL_BOTH);
        setLayoutData(gridData);
        init();

        group = new Composite(this, SWT.NONE);
        layout = new GridLayout();
        layout.marginLeft = 0;
        layout.marginTop = 0;
        layout.marginBottom = 0;
        layout.horizontalSpacing = 0;
        layout.verticalSpacing = 0;
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        layout.marginRight = 0;
        group.setLayout(layout);
        gridData = new GridData(GridData.FILL_BOTH);
        group.setLayoutData(gridData);

        treeViewer = new TreeViewer(group, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
        treeViewer.setLabelProvider(new TableLabelProvider());
        treeViewer.setContentProvider(new ContentProvider());
        tree = treeViewer.getTree();
        tree.setLinesVisible(true);
        tree.setHeaderVisible(true);
        layout = new GridLayout();
        layout.marginLeft = 0;
        layout.marginTop = 0;
        layout.marginBottom = 0;
        layout.horizontalSpacing = 0;
        layout.verticalSpacing = 0;
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        layout.marginRight = 0;
        gridData = new GridData(GridData.FILL_BOTH);
        tree.setLayoutData(gridData);
        tree.setLayout(layout);

        Composite footer = new Composite(this, SWT.NONE);
        layout = new GridLayout(6, false);
        layout.marginLeft = 0;
        layout.marginTop = 0;
        layout.marginBottom = 0;
        layout.horizontalSpacing = 0;
        layout.verticalSpacing = 0;
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        footer.setLayout(layout);
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.heightHint = 20;
        footer.setLayoutData(gridData);
        createFirstButton(footer);

        createForwardButton(footer);

        createNextButton(footer);

        createLastButton(footer);

        lblPages = new Label(footer, SWT.NONE);
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        gridData.verticalAlignment = GridData.CENTER;
        lblPages.setLayoutData(gridData);
        lblPages.setText(Messages.getString("TableViewerWithMultipage.pages")); //$NON-NLS-1$

        lblItems = new Label(footer, SWT.NONE);
        gridData.verticalAlignment = GridData.CENTER;
        gridData = new GridData(GridData.FILL_HORIZONTAL);
        lblItems.setLayoutData(gridData);
        lblItems.setText(Messages.getString("TableViewerWithMultipage.items")); //$NON-NLS-1$
        //
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createForwardButton".
     * 
     * @param footer
     */
    private void createForwardButton(Composite footer) {
        GridData gridData;
        btnPre = new Button(footer, SWT.FLAT);
        btnPre.setEnabled(true);
        btnPre.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(final SelectionEvent e) {
                nowPage--;
                addItems(initItems(items));
            }
        });
        gridData = new GridData(GridData.FILL_VERTICAL);
        btnPre.setLayoutData(gridData);
        btnPre.setImage(ImageUtil.getImage(IDenguesSharedImage.DBINFO_FORWARD));
        btnPre.setToolTipText(Messages.getString("TableViewerWithMultipage.back")); //$NON-NLS-1$
        btnPre.setText(Messages.getString("TableViewerWithMultipage.back")); //$NON-NLS-1$

    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createLastButton".
     * 
     * @param footer
     */
    private void createLastButton(Composite footer) {
        GridData gridData;
        btnLast = new Button(footer, SWT.FLAT);
        btnLast.setEnabled(true);
        btnLast.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(final SelectionEvent e) {
                nowPage = pages;
                addItems(initItems(items));
            }
        });
        gridData = new GridData(GridData.FILL_VERTICAL);
        btnLast.setLayoutData(gridData);
        btnLast.setImage(ImageUtil.getImage(IDenguesSharedImage.DBINFO_LAST));
        btnLast.setText(Messages.getString("TableViewerWithMultipage.last")); //$NON-NLS-1$
        btnLast.setToolTipText(Messages.getString("TableViewerWithMultipage.last")); //$NON-NLS-1$
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createLastButton".
     * 
     * @param footer
     */
    private void createNextButton(Composite footer) {
        GridData gridData;
        btnNext = new Button(footer, SWT.FLAT);
        btnNext.setEnabled(true);
        btnNext.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(final SelectionEvent e) {
                nowPage++;
                addItems(initItems(items));
            }
        });
        gridData = new GridData(GridData.FILL_VERTICAL);
        btnNext.setLayoutData(gridData);
        btnNext.setImage(ImageUtil.getImage(IDenguesSharedImage.DBINFO_NEXT));
        btnNext.setText(Messages.getString("TableViewerWithMultipage.next")); //$NON-NLS-1$
        btnNext.setToolTipText(Messages.getString("TableViewerWithMultipage.next")); //$NON-NLS-1$

    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createFirstButton".
     * 
     * @param footer
     */
    private void createFirstButton(Composite footer) {
        GridData gridData;
        btnFirst = new Button(footer, SWT.FLAT);
        btnFirst.setEnabled(true);
        btnFirst.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(final SelectionEvent e) {
                nowPage = 1;
                addItems(initItems(items));
            }
        });

        gridData = new GridData(GridData.FILL_VERTICAL);
        btnFirst.setLayoutData(gridData);
        btnFirst.setImage(ImageUtil.getImage(IDenguesSharedImage.DBINFO_FIRST));
        btnFirst.setText(Messages.getString("TableViewerWithMultipage.first")); //$NON-NLS-1$
        btnFirst.setToolTipText(Messages.getString("TableViewerWithMultipage.first")); //$NON-NLS-1$
    }

    public Tree getTree() {
        return tree;
    }

    private List<DataSetRow> initItems(List<DataSetRow> dataItems) {
        if (dataItems == null)
            return null;

        this.lblItems
                .setText(Messages.getString("TableViewerWithMultipage.items") + Messages.getString("TableViewerWithMultipage.item", totalItems)); //$NON-NLS-1$ //$NON-NLS-2$
        this.lblItems.pack();
        this.lblPages
                .setText(Messages.getString("TableViewerWithMultipage.pages") + Messages.getString("TableViewerWithMultipage.page", nowPage, pages)); //$NON-NLS-1$ //$NON-NLS-2$
        this.lblPages.pack();
        if (this.pageItems * this.pages - this.totalItems > 0) {
            if (this.nowPage == this.pages)
                return dataItems.subList((this.nowPage - 1) * this.pageItems, this.totalItems);
        }
        return dataItems.subList((this.nowPage - 1) * this.pageItems, this.nowPage * this.pageItems);
    }

    /**
     * @return
     */
    private boolean initPages() {
        this.pages = this.totalItems / this.pageItems;
        if (this.totalItems > this.pages * this.pageItems)
            this.pages++;
        this.nowPage = 1;

        return true;
    }

    /**
     * 
     * @return
     */
    private boolean addItems(List<DataSetRow> dataItems) {
        if (dataItems == null)
            return false;
        if (this.pages <= 1) {
            btnFirst.setEnabled(false);
            btnPre.setEnabled(false);
            btnNext.setEnabled(false);
            btnLast.setEnabled(false);
        } else if (this.nowPage == 1 && this.pages > 1) {
            btnFirst.setEnabled(false);
            btnPre.setEnabled(false);
            btnNext.setEnabled(true);
            btnLast.setEnabled(true);
        } else if (this.nowPage == this.pages && this.pages > 1) {
            this.btnFirst.setEnabled(true);
            this.btnPre.setEnabled(true);
            this.btnNext.setEnabled(false);
            this.btnLast.setEnabled(false);
        } else if (this.nowPage > 1 && this.nowPage < this.pages) {
            this.btnFirst.setEnabled(true);
            this.btnPre.setEnabled(true);
            this.btnNext.setEnabled(true);
            this.btnLast.setEnabled(true);
        }
        treeViewer.setInput(dataItems);
        return true;
    }

    /**
     * @return
     */
    private boolean show() {
        this.initPages();
        this.addItems(this.initItems(this.items));
        return true;
    }

    public TreeViewer getTreeViewer() {
        return this.treeViewer;
    }

    public void setInput(String[] colNames, List<DataSetRow> list, int itemsPage) {
        this.pageItems = itemsPage;
        if (list == null) {
            return;
        }
        clearDefaultTable();
        refreshDefaultTableHeader(colNames);
        if (list.isEmpty()) {
            return;
        }
        this.items = list;
        if (items != null) {
            this.totalItems = items.size();
        }
        show();
        updateCellEditors(colNames);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "updateCellEditors".
     */
    private void updateCellEditors(String[] colNames) {
        String[] prop_id = new String[colNames.length + 1];
        CellEditor[] cellEditors = new CellEditor[colNames.length + 1];
        for (int i = 0; i < prop_id.length; i++) {
            if (i == 0) {
                prop_id[i] = "i_d_"; //$NON-NLS-1$
            } else {
                String string2 = colNames[i - 1];
                prop_id[i] = "id_" + string2.toLowerCase(); //$NON-NLS-1$
            }
            cellEditors[i] = new TextCellEditor(tree);
        }
        if (canModify) {
            treeViewer.setCellModifier(new CellModifier());
            treeViewer.setColumnProperties(prop_id);
            treeViewer.setCellEditors(cellEditors);
        }
    }

    private void clearDefaultTable() {
        tree.clearAll(true);
        for (int i = 0; i < tree.getColumnCount(); i++) {
            tree.getColumn(i).setText(""); //$NON-NLS-1$
            tree.getColumn(i).setWidth(0);
        }
    }

    private void refreshDefaultTableHeader(String[] columns) {
        int existingColumnsLength = tree.getColumnCount();
        // update the existing columns
        for (int i = 1; i < existingColumnsLength; i++) {
            if ((i - 1) < columns.length) {
                // the column exist and must be updated
                tree.getColumn(i).setText(columns[i - 1]);
            } else {
                // the following column exist and must be dispose
                existingColumnsLength = i;
            }
        }

        // if necessary, add the another columns must be created
        for (int i = existingColumnsLength; i < columns.length + 1; i++) {
            if (i == 0) {
                new TreeColumn(tree, SWT.LEFT);
            } else {
                TreeColumn column = new TreeColumn(tree, SWT.LEFT);
                column.setText(columns[i - 1]);
            }
        }

        // if necessary, dispose the unusable columns
        while (tree.getColumnCount() > columns.length + 1) {
            tree.getColumn(tree.getColumnCount() - 1).dispose();
        }

        for (int i = 0; i < tree.getColumnCount(); i++) {
            TreeColumn col = tree.getColumn(i);
            col.addSelectionListener(new ColumnSelectionListener());
        }
    }

    private void setColumnSorting(TreeColumn column, int order) {
        tree.setSortColumn(column);
        tree.setSortDirection(order == 1 ? SWT.UP : SWT.DOWN);
    }

}
